<template>
  <div class="rightmain_container">
    <!-- 16.3这里在外面套一层盒子，因为我们需要做个内边距 -->
    <!-- 顶部搜索部分 -->
    <div class="search_box">
      <!-- 十六、实现 权限---用户列表 搜索栏 -->
      <!-- 16.1使用 element-plus 组件库里面的 Layout 布局 -->
      <el-row>
        <!-- 16.2 :span是用于计算比例 -->
        <!-- 左边标题 -->
        <!-- <el-col :sm="18" :xs="11" :xl="21" class="left_search"> -->
        <el-col :span="21" class="left_search">
          <el-icon :size="20" color="#444545">
            <Search />
          </el-icon>
          <span>筛选搜索</span>
        </el-col>
        <!-- 右边按钮 -->
        <!-- <el-col :sm="3" :xs="10" :xl="2" class="right_search"> -->
        <el-col :span="3" class="right_search">
          <el-button @click="cleanSearch">重置</el-button>
          <!-- 二十二、用户输入输入框内搜索事件 -->
          <!-- 22.1点击查询搜索按钮 -->
          <el-button type="primary" @click="requestTableData">查询搜索</el-button>
        </el-col>
      </el-row>
      <el-row class="input_search">
        <span>账号/姓名：</span>
        <!-- 16.4这里是搜索输入框，输入框要开 v-model 才就可以显示输入的内容 -->
        <!-- <el-input autocomplete="false" placeholder="请输入账号、姓名" v-model="input" /> -->
        <!-- 22.2用户回车事件，跟点击查询搜索按钮事件是一样的,v-model 绑定的应该是 keyword -->
        <el-input autocomplete="false" placeholder="请输入账号、姓名" @keyup.enter="requestTableData"
          v-model="queryParams.keyword" />
      </el-row>
    </div>

    <!-- 十七、数据列表 添加按钮 -->
    <div class="oprations_box">
      <!-- <el-row> -->
      <!-- <el-col :span="23" class="left_search">
          <el-icon :size="20" color="#444545">
            <Tickets />
          </el-icon>
          <span>数据列表</span>
        </el-col> -->
      <div class="left_search">
        <el-icon :size="20" color="#444545">
          <Tickets />
        </el-icon>
        <span>数据列表</span>
      </div>
      <div class="right_btn">
        <!-- <el-col :span="1"> -->
        <!-- 17.1给添加按钮绑定事件 -->
        <el-button type="primary" @click="showFromDialogFn">
          添加
        </el-button>
        <!-- 23.4将id初始值之为0传过去 -->
        <!-- <div class="addvbtn" @click="showFromDialogFn(0)">
          添加
        </div> -->
        <!-- </el-col> -->
      </div>
      <!-- </el-row> -->
    </div>

    <!-- 十八、用户列表 --- 表格 -->
    <!-- 18.1使用 element-plus 里面的 Table 表格 --- 带边框表格-->
    <div class="table_box">
      <!-- 18.44单行表单居中：align="center"，所有列表的居中：:cell-style="{ textAlign: 'center' }"
:header-cell-style="{ 'text-align': 'center' }"   -->
      <!-- <el-table :data="tableData" border style="width: 100%" > -->
      <el-table :data="tableData" border style="width: 100%" :cell-style="{ textAlign: 'center' }"
        :header-cell-style="{ 'text-align': 'center' }">
        <!-- 18.32这个时候 label是表头的标题，prop是存进去数据的对应英文 -->
        <el-table-column prop="id" label="编号" />
        <el-table-column prop="username" label="账号" />
        <el-table-column prop="nickName" label="姓名" />
        <el-table-column prop="email" label="邮箱" />
        <!-- 18.33时间需要进行格式化，我们可以使用方法一：  element-plus 里面的 Table表格里面的 formatter 属性用来格式化内容，方法二：npm 里面可以找第三方插件 moment 和 dayjs来实现，方法三自己 utils 文件夹 index.ts 里面封装一个格式化数据的方法   -->
        <!-- 18.34使用方法三自己定义方法,去到 utils 文件夹 index.ts -->
        <!-- <el-table-column prop="createTime" label="添加时间" /> -->
        <!-- 18.39使用插槽的形式，使用格式化时间方法 -->
        <!-- <el-table-column prop="createTime" label="添加时间"> -->
        <el-table-column prop="createTime" label="添加时间">
          <template #default="{ row }">
            <!-- 18.41使用格式化时间方法 -->
            {{ formateTime(row.createTime) }}b
          </template>
        </el-table-column>
        <!-- 18.42这里使用方法一：element-plus 里面的 Table表格里面的 formatter 属性用来格式化内容 -->
        <el-table-column prop="loginTime" label="最后登录" :formatter="formatterTime" />

        <!-- 十九、启用禁用 -->
        <!-- 19.1使用 element-plus 组件库里面的 Switch 开关 --- 扩展的 value 类型 -->
        <!-- <el-table-column prop="" label="是否启用" /> -->
        <el-table-column prop="status" label="是否启用">
          <!-- 19.2使用插槽的形式插入 -->
          <template #default="{ row }">
            <!-- 19.3这里 active-value 如果直接等于就是字符串，在前面加上：就是数字 -->
            <!-- 19.4添加点击事件，这里 el-switch 组件有事件 change 方法 -->
            <!-- <el-switch v-model="row.status" :active-value="1" :inactive-value="0" /> -->
            <!-- 19.5使用 change 方法 -->
            <!-- 19.15 switchChange 的方法将 id 和 status 传进去方法里面 -->
            <!-- <el-switch v-model="row.status" :active-value="1" :inactive-value="0"
              @change="switchChange(row.id, row.status)" /> -->
            <!-- 19.25 这里 switchChange 方法直接传入整个 row，不需要拆开，因为单独传的话数据不是响应式的 -->
            <el-switch v-model="row.status" :active-value="1" :inactive-value="0" @change="switchChange(row)" />
          </template>
        </el-table-column>

        <!-- 18.45操作是显示两个按钮的 -->
        <el-table-column prop="" label="操作">
          <!-- 18.46找到 element-plus 里面的 button按钮组件 --- 文字按钮/链接按钮 -->
          <template #default="{ row }">
            <!-- 25.12绑定点击 分配角色 的按钮事件 -->
            <el-button type="primary" link @click="showAllocRoleFn(row.id)">分配角色</el-button>
            <!-- 23.5绑定 showFromDialogFn 点击事件,把当前的id传进去 -->
            <el-button type="primary" link @click="showFromDialogFn(row.id)">编辑</el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>

    <!-- 二十、分页按钮 -->
    <!-- 20.44把国际化的分页，自己封装成一个组件 -->
    <!-- 20.48隐藏以下代码 -->
    <!-- 20.1使用 element-plus 组件库里面的 Pagination 分页 --- 附加功能  -->
    <!-- 20.2复制以下代码 -->
    <!-- <div class="pagination_container"> -->
    <!-- 20.3total总条数 sizes一页显示多少条 prev上一页 pager每个页 next下一页 jumper跳转到也页面  -->
    <!-- 20.4修改 :page-sizes -->
    <!-- 20.6prev-text next-text 分页中文只能修改这两个属性 -->
    <!-- 20.7如果要其他转中文的话，要去到 element-plus 里面的指南/国际化/ConfigProvider，添加国际化的配置 -->
    <!-- <el-pagination v-model:current-page="currentPage4" v-model:page-size="pageSize4" :page-sizes="[10, 20, 50, 100]"
        :small="small" :disabled="disabled" background layout="total, sizes, prev, pager, next, jumper" :total="354"
        @size-change="handleSizeChange" @current-change="handleCurrentChange" /> -->

    <!-- 20.9添加国际化配置 -->
    <!-- <el-config-provider :locale="zhCn"> -->
    <!-- 20.12修改 v-model:current-page 为当前页，改成 params 里面的数据绑定在这里-->
    <!-- 20.15修改total总条数 -->
    <!-- 20.16修改v-model:page-size -->
    <!-- 20.17修改 size-change 事件，点击一个页面显示多少条数 -->
    <!-- <el-pagination v-model:current-page="queryParams.pageNum" v-model:page-size="queryParams.pageSize"
          :page-sizes="[10, 20, 50, 100]" :small="small" :disabled="disabled" background
          layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handleSizeChange"
          @current-change="handleCurrentChange" /> -->
    <!-- 20.21点击事件 size-change current-change 取消使用  -->
    <!-- <el-pagination v-model:current-page="queryParams.pageNum" v-model:page-size="queryParams.pageSize"
          :page-sizes="[10, 20, 50, 100]" :small="small" :disabled="disabled" background
          layout="total, sizes, prev, pager, next, jumper" :total="total" />
      </el-config-provider>
    </div> -->

    <!-- 二十一、点击添加按钮，弹出添加账号的对话框 -->
    <!-- 21.1 usm 文件夹新建 components 文件夹里面放 editUser.vue，把自动生成的 editUser.vue 拉进去文件夹里面,去到该文件 -->
    <!-- 21.6引入该子组件 -->
    <!-- 21.7因为组件一开始是隐藏的，所以我们去到 usm 文件夹新建 components 文件夹里面放 editUser.vue ，里面设置显示  -->
    <!-- 21.26传一个 show 变量给子组件 -->
    <!-- 17.6这个时候定义一个子组件修改父组件的方法,Daliog对话框组件里面提供 close 的方法  -->
    <!-- 17.7去到 editUser.vue 文件 -->
    <!-- :show="showFromDialog"  -->
    <!-- 23.6将id传给子组件 -->
    <!-- 23.7去到子组件 editUser.vue 文件 里面接收父组件的传过来的id -->
    <editUser ref="editUser_ref" @close="closeFromDialogFn" :id="currentUserId" />

    <!-- 25.4引入子组件 点击分配角色用的弹出框 Dialog -->
    <!-- 25.5父组件控制子组件的显隐,所以这里父组件传 show 进去子组件里面 -->
    <!-- 25.18将当前的id也传到子组件,因为子组件需要获取到当前用户的角色,去到子组件 allocRole.vue -->
    <!-- 25.70定义子传父的事件 closeAllocRoleDialogFn -->
    <allocRole :show="showAllocRoleDialog" :id="allocRoleCurrentId" @close="closeAllocRoleDialogFn" />

    <!-- 分页封装 -->
    <!-- <Pagination :currentPage="queryParams.pageNum" :pageSize="queryParams.pageSize" :pageAllSizes="[10, 20, 50, 100]"
      @pageChange="pageChange" @sizeChange="sizeChange" :total="total" :background="true" /> -->


    <!-- 26.7父传子数据 -->
    <!-- 26.8去到分页组件 PaginationTwo.vue  -->
    <PaginationTwo v-model:current-page="queryParams.pageNum" v-model:page-size="queryParams.pageSize"
      :page-sizes="[10, 20, 50, 100]" :total="total" />
  </div>
</template>

<script setup lang='ts'>
// 16.2导入图标
import { ApiGetUserList, ApiUpdateStatus } from '@/http/api';
import { Search, Tickets } from '@element-plus/icons-vue'
import { reactive, ref, toRefs, watch } from 'vue';

// 18.40导入 utils 里面格式化时间的方法
import { formateTime } from '@/utils/index'
import editUser from '@/views/ums/components/editUser.vue'
// @ts-ignore
import { ElMessage } from 'element-plus'
import allocRole from '@/views/ums/components/allocRole.vue';

// 20.46隐藏以下代码
// 20.8引入国际化配置
// @ts-ignore
// import zhCn from 'element-plus/dist/locale/zh-cn.mjs'

import axios, { CancelTokenSource } from 'axios'
// import Pagination from '@/components/Pagination/Pagination.vue'

// 26.6导入分页组件
import PaginationTwo from '@/components/Pagination/PaginationTwo.vue'

// const pageAllSizes = [10, 20, 50, 100]

// 18.2导入表格数据
// 18.3请求接口返回的数据替换以下数据，去到 http 文件夹里面的 api.ts文件
// const tableData = [
//   {
//     date: '2016-05-03',
//     name: 'Tom',
//     address: 'No. 189, Grove St, Los Angeles',
//   },
//   {
//     date: '2016-05-02',
//     name: 'Tom',
//     address: 'No. 189, Grove St, Los Angeles',
//   },
//   {
//     date: '2016-05-04',
//     name: 'Tom',
//     address: 'No. 189, Grove St, Los Angeles',
//   },
//   {
//     date: '2016-05-01',
//     name: 'Tom',
//     address: 'No. 189, Grove St, Los Angeles',
//   },
// ]

// 18.30定义 tableData 的数据，使用 ref 去存响应式数据
const tableData = ref<IUserListModel[]>([])

// 18.12定义传进去的参数数据
const queryParams = reactive<IQueryUserListParams>({
  // 关键字
  keyword: '',
  // 显示第一页
  pageNum: 1,
  // 一页展示10条数据
  pageSize: 10
})

// 17.37定义当前id currentUserId 为响应式数据,当前用户的id,用于编辑用户时显示编辑用户页面的
const currentUserId = ref(0)


// 20.13定义总条数的响应式数据
const total = ref(0)

// 20.35在全局定义一个source对象，方便我们在 watch 里面使用它来取消请求
let source: CancelTokenSource;
// 20.36定义一个变量，标记 请求是否正在进行
let getting = false;

// 17.24以下方法封装起来
// 18.13调用接口请求数据
// ApiGetUserList(queryParams).then(res => {
//   // 18.14如果请求成功
//   if (res.code === 200) {
//     // 18.15打印查看 res
//     // console.log(res);
//     // console.log(res.data.list);
//     // 18.16 copy object 然后去到 types 文件夹里面的 api.d.ts 约束接口返回的数据类型
//     // 18.31将 res.data.list
//     tableData.value = res.data.list
//   }

// })

// 17.25封装
const requestTableData = () => { 
  // 20.38请求正在进行
  getting = true;
  // 20.39每次发请求，都新建一个 source 对象
  source = axios.CancelToken.source();// 创建CancelToken对象，用于标记请求是否已经发送过来了。

  // 20.40然后把 sourc的token 值，传递给请求的config
  // 20.41去到 http 文件夹里面的 http.ts
  ApiGetUserList(queryParams, { cancelToken: source.token }).then(res => {
    if (res.code === 200) {
      tableData.value = res.data.list;
      // 修改数据总数
      total.value = res.data.total;
      // 请求结束
    }
  }).finally(() => {
    getting = false;
  })


  // 20.37隐藏以上代码
  // 18.14如果请求成功

  // ApiGetUserList(queryParams).then(res => {
  // if (res.code === 200) {
  //   // 18.15打印查看 res
  //   // console.log(res);
  //   // console.log(res.data.list);
  //   // 18.16 copy object 然后去到 types 文件夹里面的 api.d.ts 约束接口返回的数据类型
  //   // 18.31将 res.data.list 
  //   tableData.value = res.data.list;

  //   // 20.14修改数据总数
  //   total.value = res.data.total;
  // }
  // })
}

// 19.26一开始先请求一次数据
requestTableData()

// 18.43定义格式化时间的formatter
// const formatterTime = (row, column, cellValue, index) => {
const formatterTime = (row: any) => {
  if (!row.loginTime) {
    return "";
  }
  // const d = new Date(row.loginTime);
  // return `${d.getFullYear()}-${(d.getMonth() + 1)
  //   .toString().padStart(2, "0")}-${d.getDate().toString().padStart(2, "0")}`; //2021-07-16

  return row.loginTime.replace('T', ' ').substring(0, 19)
}

// 19.6定义修改禁用启用的方法
// 19.7修改帐号状态需要请求接口，去到 http 文件夹里面的 api.ts
// const switchChange = (val: number) => {
// 19.16定义 id 和 status 的形参
// const switchChange = (id: number, status: number) => {
// 19.26这里只需要一个形参
const switchChange = (user: IUserListModel) => {
  // 19.17查看是否点击的时候能够获取到当前点击的 id 和 status
  // console.log(id, status);
  // 19.18请求接口
  // 19.19去到 http 文件夹里面的 api.ts
  // console.log(user.id);
  // console.log(typeof (user.id));

  ApiUpdateStatus({
    // id, status
    // 19.27这边分别赋值给 id 和 status
    id: user.id,
    status: user.status
  }).then(res => {
    // 19.28如果请求成功返回 code === 200
    if (res.code === 200) {
      console.log(res);

      // 19.29弹框提示用户
      ElMessage({
        type: 'success',
        message: '修改用户状态成功'
      })
    } else {
      // 19.30把 switch 回归原来的效果
      user.status = user.status === 1 ? 0 : 1
    }
  })
}

// 21.27添加和编辑的弹出
// 21.28一开始定义为false，为隐藏的状态
// 21.29去到 ums 文件夹里面的 components 文件夹里面的 editUser.vue 
// const showFromDialog = ref(false)

// 17.2点击添加的按钮，修改当前状态为显示 true,这个时候会发现，点击第一次添加为显示，关闭之后，再点添加按钮，就不会再弹出对话框 Dialog
// 17.3所以这个时候需要先去处理点击关闭 dialog 的状态
let editUser_ref = ref<any>(null)

// 二十三、处理 Dialog 弹出的是点击 添加按钮还是编辑按钮
// 23.1因为我们显示这个Dialog弹出框的时候，如果是编辑的话是需要传id的，所以我们在点开的时候统一，无论是添加弹出Dialog还是编辑按钮弹出，我们都传id
// const showFromDialogFn = () => {
// 23.2定义id的形参
const showFromDialogFn = (id: number) => {
  // showFromDialog.value = true;
  editUser_ref.value.show = true;
  // 23.3将当前id的值响应式赋值给id，给编辑按钮绑定 showFromDialogFn 事件
  currentUserId.value = id;
}

// 17.4关闭 Dialog 对话框的方法
// const closeFromDialogFn = () => {
// 17.22定义形参接受子组件传过来的变量
const closeFromDialogFn = (isReload: boolean) => {
  // 17.5修改父组件控制 Dialog 显示的变了
  // showFromDialog.value = false;
  editUser_ref.value.show = false;

  // 23.43点击事件结束之后，需要把id赋值清空，改回0,回到初始状态,去到 editUser.vue 
  currentUserId.value = 0

  if (isReload) {
    // 17.23重新调用接口，请求Table表格数据,先去封装请求接口的方法
    // 17.27调用接口
    // 17.28去到 editUser.vue 文件
    requestTableData();
  }
}

// 输入搜索内容
// const input = ref('')

// 20.2把对应的方法拿过来
// const currentPage4 = ref(4)
// 20.5修改 pageSize4
// const pageSize4 = ref(10)

// 20.45隐藏以下代码
// const small = ref(false)
// const disabled = ref(false)

// 20.18绑定点击一页的内容容量的事件
// const handleSizeChange = (val: number) => {
// const handleSizeChange = () => {
//   // console.log(`${val} items per page`)
//   // 20.19重新调用接口，获取 Table 页面
//   // 20.20这里 element-plus 不推荐使用 current-page 和 page-size 改变，推荐使用 v-model 双向数据
//   requestTableData();
// }

// 20.10点击页码跳转的事件
// const handleCurrentChange = (val: number) => {
// const handleCurrentChange = () => {
//   // console.log(`current page: ${val}`)
//   // 20.11重新调用接口，获取 Table 页面
//   requestTableData();
// }

// 25.6定义show的控制显隐数据,初始值设置为 false,默认是关闭的
// 25.7回去子组件 allocRole.vue 接收父组件的数据
const showAllocRoleDialog = ref(false)

// 25.15跟点击 编辑 按钮的逻辑是一样的,需要传当前的id进去
const allocRoleCurrentId = ref(0);

// 25.13定义点击分配角色事件
const showAllocRoleFn = (id: number) => {
  // 25.14点击的时候将 Dialog 的显隐状态改为 true
  showAllocRoleDialog.value = true
  // 25.16查看是否可以获取到当前的id
  // console.log(id);
  // 25.17将传进来的当前的id 赋值给 id
  allocRoleCurrentId.value = id;

}

// 25.71定义关闭 角色分配的弹出框
const closeAllocRoleDialogFn = () => {
  // 25.72点击关闭的时候，把 dialog 关闭
  showAllocRoleDialog.value = false
  // 25.73将当前id改为0
  allocRoleCurrentId.value = 0
}

// 20.48添加点击修改的方法
// 点击切换当前页
// const pageChange = (num: number) => {
//   // 避免重复请求
//   if (getting) {
//     getting = false;
//     // axios取消上一个请求
//     source.cancel();
//   }
//   queryParams.pageNum = num
//   requestTableData()
// }

// 20.48点击切换一页最大显示条数
// const sizeChange = (val: number) => {
//   queryParams.pageSize = val
//   requestTableData()
// }

// 20.22从 queryParams 解构 pageNum, pageSize
let { pageNum, pageSize } = toRefs(queryParams)
// 20.23监听页码发生变化
// watch([pageNum, pageSize], (newVal, oldVal) => {
watch([pageNum, pageSize], () => {
  // console.log(newVal, oldVal);
  // 20.29解决请求页码发生2次请求，这个是方法一:
  // 20.24如果新值跟旧值不是同一个
  // if (newVal[0] !== oldVal[0]) {
  //   // 20.25发送请求
  //   requestTableData()
  // } else
  //   // 20.26当前页码不变，但是我们点击 页容量的变化
  //   if (newVal[0] === oldVal[0] && newVal[1] !== oldVal[1]) {
  //     // console.log('当前页码不变，但是我们点击 页容量的变化')
  //     // 20.27发送请求
  //     // 20.28去到 http 文件夹里面的 api.ts
  //     requestTableData();
  //   }

  // 20.30这个是方法二:
  // 防抖的话队列可能会消失
  // 如果采用节流： 是size先变化 , 不能采用节流 ， 如果第一次发送了请求， num: 41 , size: 20 ,后端是没有数据的
  // console.log(newVal, oldVal)
  // 可以选择采用防抖 -- 正确的 -- 大部分情况下是可行的

  // 20.31方法三：使用取消请求
  // 20.32去到 api.ts
  // 如果获取表格数据的请求正在进行，就把上一个请求取消

  // 20.47隐藏以下的代码
  if (getting) {
    getting = false;
    // axios取消上一个请求
    source.cancel();
  }
  // 把上一个取消后，再发送下一个请求
  requestTableData()

})

// 点击重置按钮事件
const cleanSearch = () => {
  // 点击的时候将关键字改为空 ''
  queryParams.keyword = ''
  // 然后重新发送接口请求
  requestTableData()
}

</script>
<style lang="less" scoped>
.rightmain_container {

  // 顶部搜索部分
  .search_box {
    padding: 20px;
    box-shadow: 0 0 4px 2px #eee;
    margin-bottom: 20px;
    border-radius: 2px;
    // 二十四、将搜索框部分粘性定位到顶部
    position: sticky;
    top: -20px;
    z-index: 2;
    background-color: rgba(255, 255, 255, 0.9);

    // 右边按钮
    .right_search {
      display: flex;
      align-items: center;
      justify-content: right;
    }

    .input_search {
      display: flex;
      align-items: center;

      span {
        font-size: 14px;
        color: #606266;
        margin-left: 30px;
      }

      .el-input {
        width: 200px;
      }
    }
  }


  // 左边标题
  .left_search {
    display: flex;
    align-items: center;


    span {
      font-size: 14px;
      color: #303133;
      margin-left: 10px;
    }
  }

  // 数据列表
  .oprations_box {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 20px;
    box-shadow: 0 0 4px 2px #eee;
    margin-bottom: 20px;
    border-radius: 2px;

    // .addvbtn {
    //   padding: 8px 15px;
    //   font-size: 14px;
    //   color: #fff;
    //   background-color: #409eff;
    //   border-radius: 4px;
    //   cursor: pointer;
    // }

  }

  // 分页按钮
  // .pagination_container {
  //   display: flex;
  //   justify-content: right;
  //   margin-top: 20px;
  // }


}

// 搜索盒子
</style>